﻿using System;
using System.ServiceModel;
using DataService.Dataset;
using Types.Attributes;
using Types.CustomTypes;

namespace Services
{
    abstract partial class BusinessBase
    {
        #region [CONSTRUCTORS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        public BusinessBase()
        {
            try
            {
                if (OperationContext.Current == null || OperationContext.Current.IncomingMessageHeaders == null)
                {
                    mContextMetaData = new CContextMetaData();
                    return;
                }
                int contextHeaderIndex = OperationContext.Current.IncomingMessageHeaders.FindHeader(ContextParameters.ContextMetaDataName, ContextParameters.ContextMetaDataNamespace);
                if (contextHeaderIndex > 0)
                {
                    mContextMetaData = OperationContext.Current.IncomingMessageHeaders.GetHeader<CContextMetaData>(contextHeaderIndex);
                }
            }
            catch
            {
                mContextMetaData = new CContextMetaData();
                throw;
            }
        }

        #endregion

        #region [COMMON MEMBERS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// Used to transport web side session information to services for each call
        /// </summary>
        private CContextMetaData mContextMetaData = null;

        public string ProcessorUserID
        {
            get
            {
                string processorId = string.Empty;

                //TODO: Get processor person unique id

                return processorId;
            }
        }

        public CContextMetaData ContextData
        {
            get
            {
                if (mContextMetaData == null)
                {
                    mContextMetaData = new CContextMetaData(); // throw new Exception("Context Data is empty");
                }
                return mContextMetaData;
            }
        }

        string _pDefaultDB = string.Empty;
        private string pGetDefaultDB()
        {
            if (_pDefaultDB == string.Empty && this.GetType().IsDefined(typeof(DefaultDatabaseAttribute), false))
            {
                DefaultDatabaseAttribute dbAttribute = (DefaultDatabaseAttribute)this.GetType().GetCustomAttributes(typeof(DefaultDatabaseAttribute), false)[0];
                _pDefaultDB = dbAttribute.mDatabaseName;
            }
            return _pDefaultDB;
        }

        #endregion

        //----------------------------------------------------------------------------------------

        #region [CLASS BASE MEMBERS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private IDataObject mDataObject;

        private bool mIsRoot = false;
        public int nestedLevel = 1;

        protected bool IsRoot
        {
            get
            {
                if (nestedLevel > 1)
                {
                    nestedLevel--;
                    return false;
                }
                return mIsRoot;
            }
        }

        #endregion

        #region [METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        protected IDataObject GetDataObject(bool pIsTransactional, string pDefaultDB = null)
        {
            string defaultDB = string.IsNullOrEmpty(pDefaultDB) ? pGetDefaultDB() : pDefaultDB;
            if (mDataObject == null)
            {
                mDataObject = DataObjectFactory.GetDataObject(defaultDB, pIsTransactional);
                if (pIsTransactional)
                    mIsRoot = true;
            }
            else
            {
                //Used to call more then one methods which are in same BS and each of them is in separate transaction
                if (mDataObject.IsDataObjectTransactional && mIsRoot && pIsTransactional)
                {
                    nestedLevel++;
                    return mDataObject;

                }
                if (mDataObject.IsDataObjectTransactional && mIsRoot && !pIsTransactional)
                {
                    return mDataObject;
                }
                else if (mDataObject.IsDataObjectTransactional && !mIsRoot)
                {
                    return mDataObject;
                }
                else
                {
                    mDataObject = DataObjectFactory.GetDataObject(defaultDB, pIsTransactional);
                    if (pIsTransactional)
                        mIsRoot = true;
                }
            }
            return mDataObject;
        }

        //If service has special connection information
        protected IDataObject GetServiceDataObject(bool pIsTransactional)
        {
            string connectionStringName = string.Empty;

            if (this.GetType().IsDefined(typeof(ServiceConnectionNameAttribute), false))
            {
                ServiceConnectionNameAttribute cnAttribute = (ServiceConnectionNameAttribute)this.GetType().GetCustomAttributes(typeof(ServiceConnectionNameAttribute), false)[0];
                connectionStringName = cnAttribute.mConnectionName;
            }

            if (connectionStringName == string.Empty)
            {
                return GetDataObject(pIsTransactional);
            }

            if (mDataObject == null)
            {
                mDataObject = DataObjectFactory.GetAnotherDataObject(connectionStringName, pGetDefaultDB(), pIsTransactional);
                if (pIsTransactional)
                    mIsRoot = true;
            }
            else
            {
                //Used to call more then one methods which are in same BS and each of them is in separate transaction
                if (mDataObject.IsDataObjectTransactional && mIsRoot && pIsTransactional)
                {
                    nestedLevel++;
                    return mDataObject;

                }
                if (mDataObject.IsDataObjectTransactional && mIsRoot && !pIsTransactional)
                {
                    return mDataObject;
                }
                else if (mDataObject.IsDataObjectTransactional && !mIsRoot)
                {
                    return mDataObject;
                }
                else
                {
                    mDataObject = DataObjectFactory.GetAnotherDataObject(connectionStringName, pGetDefaultDB(), pIsTransactional);
                    if (pIsTransactional)
                        mIsRoot = true;
                }
            }
            return mDataObject;
        }

        protected IDataObject GetDataObject()
        {
            return GetDataObject(false);
        }

        protected T CreateBusinessObject<T>() where T : BusinessBase
        {
            T objBusiness = default(T);
            objBusiness = Activator.CreateInstance<T>();
            if ((mDataObject != null) && (mDataObject.IsDataObjectTransactional))
            {
                objBusiness.mDataObject = this.mDataObject;
                objBusiness.mIsRoot = false;
            }
            return objBusiness;
        }

        #endregion
    }


}
